
#ifndef __APR_BUCKET_BRIGADE_H
#define __APR_BUCKET_BRIGADE_H

#include <apr-1/apr.h>
#include <apr-1/apr_buckets.h>
#include "APRRWMutex.h"

/** A wrapper class around the 'bucket brigade' concept from the Apache
 * Portable Runtime. This class creates a FIFO pipe that can be used in
 * a variety of situations, including for encryption, file operations, sockets
 * and some types of data rearrangement. This is because data from different
 * sources can be expressed simply by inserting by reference into the stream.
 * A more thorough explanation of the bucket brigade concept can be found in
 * the Apache Portable Runtime documentation.
 */
class APRBucketBrigade
{
   public:
      APRBucketBrigade(apr_pool_t *imp);
      ~APRBucketBrigade();
      void addSocketBucket(apr_socket_t *tsock);
      void writeEOS();
      apr_size_t writeFileToStream(const char *filename, apr_size_t start, apr_size_t end, int isbinary);
      apr_size_t writefToStream(const char *format, ...);
      apr_size_t writeToStream(const char *data, apr_size_t numbytes);
      apr_size_t readFromStream(char *data, apr_size_t numbytes);
      apr_size_t removeFromStream(apr_size_t numbytes);
      apr_size_t popFromStream(char *data, int numbytes);
      /** Has the end of the stream been reached?
       * 
       * @return
       * Non-zero if the end-of-stream marker has been reached.
       * Zero otherwise. */
      int isClosed()
         { return (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bucketbrgde))); }
      /** How much space is left that can be written to?
       * 
       * @return
       * Maximum amount of data in bytes that can be suffixed to this stream. */
      apr_size_t packedSpaceRemaining()
         { return ((isClosed()) ? 0 : (apr_size_t)-1); }
      /** How much data is in this stream?
       * 
       * @return
       * Amount of data in bytes in this stream. */
      apr_size_t realLength()
      {
         apr_status_t status;
         apr_off_t offt;
         status = apr_brigade_length(bucketbrgde, 0, &offt);
         CheckAPRError(status);
         return (apr_size_t)offt;
      }
      /** Does this bucket brigade have data?
       * 
       * @return
       * Non-zero if there is data that can be read in the stream, zero if
       * there is no data or the stream has ended. */
      int hasData()
      {
         return (!((APR_BRIGADE_EMPTY(bucketbrgde)) ||
                  (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bucketbrgde)))));
      }
      int bufferToBucketBrigade(const char *buffer, size_t *length, size_t smallblock);
      int bucketBrigadeToBuffer(char **buffer, size_t *length);
      /** A read/write lock that ensures that the bucket brigade can be used
       * as a pipe between threads. */
      APRRWMutex *rwlock;
   private:
      APRBucketBrigade(APRBucketBrigade &o) {}
      APRBucketBrigade &operator=(APRBucketBrigade &o) { return *this; }
      /** Get the raw APR bucket allocator. */
      apr_bucket_alloc_t *directlyGetBucketAllocator() { return bucketalloc; }
      /** Get the raw APR bucket brigade. */
      apr_bucket_brigade *directlyGetBucketBrigade() { return bucketbrgde; }
      /** Add the size of this bucket to the total apparent size of the bucket
       * brigade data. */
      void addBucketSize(apr_bucket *bucket) { csize += bucket->length; }
      /** The parent memory pool of the bucket brigade class. */
      apr_pool_t *parent_mp;
      /** The memory pool used by the bucket brigade class. */
      apr_pool_t *mp;
      /** The bucket allocator used by the bucket brigade class. */
      apr_bucket_alloc_t *bucketalloc;
      /** The bucket brigade type from the APR library. */
      apr_bucket_brigade *bucketbrgde;
      /** The total size of all of the length-ascertainable parts of the bucket brigade. */
      apr_size_t csize;
};

#endif
